home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Simple Slideshow / TIFF code folder / tiffinfo.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-10-15  |  29.9 KB  |  1,285 lines  |  [TEXT/CWIE]

  1. /*
  2.  * manipulate TIFF files.
  3.  */
  4. #include "tiffinfo.h"
  5. //#include "string.h"
  6.  
  7. #ifdef TIFF_PRINTFS
  8. #include <stdio.h>
  9. #endif
  10.  
  11. #include <GestaltEqu.h>
  12.  
  13.  
  14. void             TIFFError                        (TIFFPtr, OSErr, StringPtr);
  15. long             ttohs                            (TIFFPtr, short);
  16. long             ttohl                            (TIFFPtr, long);
  17. OSErr             tiff_read                        (TIFFPtr, unsigned long, void *, long);
  18. OSErr             ScanTIFFDirectory                (TIFFPtr);
  19. OSErr             ReadTIFFEntry                    (TIFFPtr, long offset, struct TIFFEntry *);
  20. long             ParseScalar                        (TIFFPtr, struct TIFFEntry *);
  21. long *            ParseArray                        (TIFFPtr, struct TIFFEntry *);
  22. CTabHandle         TranslateColorMap                (long *colorMap, short ncolors);
  23. CTabHandle         MakeGrayTable                    (short depth, short zeroIsWhite);
  24. Ptr             ThreeToFour                        (Ptr in, long nPixels);
  25.  
  26. // NA - added to convert 16 bit pixels to 8 bits
  27. Ptr             TwoToOneByte                    (TIFFPtr ti, Ptr in, long nPixels);
  28.  
  29. long             RowBytes                        (TIFFPtr);
  30. void             CopyPMStrip                        (PixMapHandle pm, Rect sr, Rect dr, short dither);
  31. void             CopyBMStrip                        (BitMap *, Rect sr, Rect dr, short inv);
  32. void             UnDifference                    (Ptr p, long rowbytes, long nrows, long span);
  33. Boolean         HasQD32                            ( void );
  34. Boolean         HasColorQD                        ( void );
  35. static long     GetGestaltResult                ( OSType gestaltSelector );
  36. PixMapHandle     RGBPixMap                        (short depth, long x, long y, long width, long height,
  37.                                                       Ptr data, long rowbytes);
  38. void             ScaleRect                        (Rect *aref, Rect *a, Rect *bref, Rect *b);
  39. Ptr             ReadStrip                        (TIFFPtr ti, long startline, long *firstread, long *linesread);
  40.  
  41.  
  42. /*
  43.  * Scan a TIFF file's fields to ferret out interesting information.
  44.  * Returns a TIFFPtr whether it succeeded or failed; the caller must
  45.  * call GetTIFFError() to distinguish.
  46.  */
  47. TIFFPtr
  48. ScanTIFF(short ref)
  49. {
  50.     struct TIFFHeader header;
  51.     TIFFPtr ti;
  52.     
  53.     ti = (TIFFPtr) NewPtrClear(sizeof(*ti));
  54.     if(ti == 0)
  55.         return(0);
  56.     
  57.     ti->ref = ref;
  58.     
  59.     if(tiff_read(ti, 0L, &header, sizeof(header)) != 0)
  60.         return(ti);
  61.     
  62.     ti->byteOrder = header.byteOrder;
  63.     if((ti->byteOrder != BigEndian && ti->byteOrder != LittleEndian) ||
  64.        ttohs(ti, header.versionNumber) != 42){
  65.            TIFFError(ti, -1, "\pNot a TIFF file.");
  66.         return(ti);
  67.     }
  68.     
  69.     ti->offset = ttohl(ti, header.offset);
  70.     ScanTIFFDirectory(ti);
  71.     
  72.     return(ti);
  73. }
  74.  
  75. void
  76. DisposeTIFF(TIFFPtr ti)
  77. {
  78.     if(ti){
  79.         if(ti->bitsPerSample)
  80.             DisposPtr( ( Ptr ) ti->bitsPerSample);
  81.         ti->bitsPerSample = 0;
  82.         if(ti->colorMap)
  83.             DisposCTable(ti->colorMap);
  84.         ti->colorMap = 0;
  85.         if(ti->stripOffsets)
  86.             DisposPtr( ( Ptr ) ti->stripOffsets);
  87.         ti->stripOffsets = 0;
  88.         if(ti->stripByteCounts)
  89.             DisposPtr( ( Ptr ) ti->stripByteCounts);
  90.         ti->stripByteCounts = 0;
  91.         DisposPtr( ( Ptr )  ti);
  92.     }
  93. }
  94.  
  95. OSErr
  96. ScanTIFFDirectory(TIFFPtr ti)
  97. {
  98.     unsigned short nentries, entryno;
  99.     struct TIFFEntry entry;
  100.     long nextoffset, n, i, offset, *colorMap = 0;
  101.     OSErr err;
  102.     
  103.     offset = ti->offset;
  104.     ti->err = 0;
  105.     
  106.     /* assign default values */
  107.     ti->bitsPerSample = (long *) NewPtr(sizeof(long));
  108.     if(ti->bitsPerSample == 0){
  109.         TIFFError(ti, err = MemError(), "\pOut of memory");
  110.         return(err);
  111.     }
  112.     ti->bitsPerSample[0] = 1;
  113.     ti->colorMap = 0;
  114.     ti->compression = NoCompression;
  115.     ti->predictor = NoPredictor;
  116.     ti->imageLength = -1;
  117.     ti->imageWidth = -1;
  118.     ti->photometricInterpretation = -1;
  119.     ti->planarConfiguration = PCContiguous;
  120.     ti->rowsPerStrip = 0x7fffffff;
  121.     ti->samplesPerPixel = 1;
  122.     ti->stripByteCounts = 0;
  123.     ti->stripOffsets = 0;    
  124.     
  125.     if((err = tiff_read(ti, ti->offset, &nentries, sizeof(nentries))) != 0)
  126.         return(err);
  127.     offset += sizeof(nentries);
  128.     nentries = ttohs(ti, nentries);
  129.     
  130. #define ZAP(x) {if(x){DisposPtr( ( Ptr ) x);} x = 0;} 
  131.     
  132.     for(entryno = 0; entryno < nentries; entryno++){
  133.         if((err = ReadTIFFEntry(ti, offset, &entry)) != 0)
  134.             return(err);
  135.         offset += sizeof(entry);
  136.         
  137.         switch(entry.tag){
  138.         case ImageWidth:
  139.             ti->imageWidth = ParseScalar(ti, &entry);
  140.             break;
  141.         case ImageLength:
  142.             ti->imageLength = ParseScalar(ti, &entry);
  143.             break;
  144.         case BitsPerSample:
  145.             ZAP(ti->bitsPerSample );
  146.             ti->bitsPerSample = ParseArray(ti, &entry);
  147.             break;
  148.         case ColorMap:
  149.             if(ti->colorMap)
  150.                 DisposCTable(ti->colorMap);
  151.             ti->colorMap = 0;
  152.             colorMap = ParseArray(ti, &entry);
  153.             if(colorMap){
  154.                 ti->colorMap = TranslateColorMap(colorMap, entry.length / 3);
  155.                 DisposPtr( ( Ptr )  colorMap);
  156.                 colorMap = 0;
  157.                 if(ti->colorMap == 0)
  158.                     TIFFError(ti, -1, "\pCould not make color map.");
  159.             }
  160.             break;
  161.         case Compression:
  162.             ti->compression = ParseScalar(ti, &entry);
  163.             break;
  164.         case Predictor:
  165.             ti->predictor = ParseScalar(ti, &entry);
  166.             break;
  167.         case PhotometricInterpretation:
  168.             ti->photometricInterpretation = ParseScalar(ti, &entry);
  169.             break;
  170.         case StripByteCounts:
  171.             ZAP(ti->stripByteCounts);
  172.             ti->stripByteCounts = ParseArray(ti, &entry);
  173.             break;
  174.         case StripOffsets:
  175.             ZAP(ti->stripOffsets);
  176.             ti->stripOffsets = ParseArray(ti, &entry);
  177.             break;
  178.         case SamplesPerPixel:
  179.             ti->samplesPerPixel = ParseScalar(ti, &entry);
  180.             break;
  181.         case RowsPerStrip:
  182.             ti->rowsPerStrip = ParseScalar(ti, &entry);
  183.             break;
  184.         case PlanarConfiguration:
  185.             ti->planarConfiguration = ParseScalar(ti, &entry);
  186.             break;
  187. #ifdef TIFF_PRINTFS
  188.         default:
  189.             printf("tag %d, type %d, length %ld, value %ld/0x%lx\n",
  190.                 entry.tag, entry.type, entry.length, entry.offset, entry.offset);
  191. #endif
  192.         }
  193.     }
  194.     
  195.     /* Some NeXT programs generates bogus PlanarConfigurations of 2. */
  196.     if(ti->samplesPerPixel == 1 && ti->planarConfiguration == 2)
  197.         ti->planarConfiguration = 1;
  198.     
  199.     if(ti->rowsPerStrip > ti->imageLength)
  200.         ti->rowsPerStrip = ti->imageLength;
  201.     ti->stripsPerImage = (ti->imageLength + ti->rowsPerStrip - 1) / ti->rowsPerStrip;
  202.  
  203.     /*
  204.      * We need a color map for gray-scale images deeper than one bit.
  205.      */
  206.     if(ti->colorMap == 0 &&
  207.        ti->samplesPerPixel == 1 &&
  208.        ti->bitsPerSample[0] <= 8 &&
  209.        ti->bitsPerSample[0] > 1 &&
  210.        (ti->photometricInterpretation == PIZeroIsWhite ||
  211.         ti->photometricInterpretation == PIZeroIsBlack)){
  212.         ti->colorMap = MakeGrayTable(ti->bitsPerSample[0],
  213.                                      ti->photometricInterpretation == PIZeroIsWhite);
  214.         if(ti->colorMap == 0)
  215.             TIFFError(ti, -1, "\pCould not make gray-scale color map.");
  216.     }
  217.         
  218.     tiff_read(ti, offset, &nextoffset, sizeof(nextoffset));
  219.     ti->nextOffset = ttohl(ti, nextoffset);
  220.     
  221.     return(0);
  222. }
  223.  
  224. /*
  225.  * returns 0 if OK, or a negative error #.
  226.  */
  227. OSErr
  228. ReadTIFFEntry(TIFFPtr ti, long offset, struct TIFFEntry *ep)
  229. {
  230.     OSErr err;
  231.     
  232.     if((err = tiff_read(ti, offset, ep, sizeof(struct TIFFEntry))) != 0)
  233.         return(err);
  234.     ep->tag = ttohs(ti, ep->tag);
  235.     ep->type = ttohs(ti, ep->type);
  236.     ep->length = ttohl(ti, ep->length);
  237.     return(0);
  238. }
  239.  
  240. /*
  241.  * tries to interpret an entry as a single 32-bit integer value.
  242.  * can handle either TIFF_SHORT or TIFF_LONG.
  243.  * assumes ReadTIFFEntry() has already adjusted byte order of tag, type, and length.
  244.  * returns 0 if OK, or a negative error #.
  245.  * On the Mac, values appear in the highest bits of offset.
  246.  */
  247. long
  248. ParseScalar(TIFFPtr ti, struct TIFFEntry *ep)
  249. {
  250.     if(ep->length != 1)
  251.         return(-1);
  252.     if(ep->type == TIFF_BYTE){
  253.         return((ep->offset >> 24) & 0xff);
  254.     } else if(ep->type == TIFF_SHORT){
  255.         return(ttohs(ti, (ep->offset >> 16) & 0xffff));
  256.     } else if(ep->type == TIFF_LONG){
  257.         return(ttohl(ti, ep->offset));
  258.     } else {
  259.         TIFFError(ti, -1, "\pUnknown value type.");
  260.         return(-1);
  261.     }
  262. }
  263.  
  264. /*
  265.  * Returns an array of longs, no matter what the size of the array element.
  266.  * Note that if the array fits in the offset field, it will be there, in the
  267.  * high-order bits, with element zero highest.
  268.  */
  269. long *
  270. ParseArray(TIFFPtr ti, struct TIFFEntry *ep)
  271. {
  272.     long nbytes, i;
  273.     unsigned char *cp = 0;
  274.     unsigned short *sp = 0;
  275.     long *lp = 0;
  276.     
  277.     if(ep->length < 0)
  278.         goto bad;
  279.         
  280.     lp = (long *) NewPtr(ep->length * sizeof(long));
  281.     if(lp == 0)
  282.         goto bad;
  283.         
  284.     if(ep->type == TIFF_BYTE){
  285.         nbytes = ep->length;
  286.         if(nbytes <= 4){
  287.             for(i = 0; i < ep->length; i++)
  288.                 lp[i] = (ep->offset >> (24 - (i * 8))) & 0xff;
  289.         } else {
  290.             cp = (unsigned char *) NewPtr(nbytes);
  291.             if(cp == 0)
  292.                 goto bad;
  293.             if(tiff_read(ti, ttohl(ti, ep->offset), (char *)cp, nbytes) != 0)
  294.                 goto bad;
  295.             for(i = 0; i < ep->length; i++)
  296.                 lp[i] = cp[i];
  297.         }
  298.     } else if(ep->type == TIFF_SHORT){
  299.         nbytes = ep->length * 2;
  300.         if(nbytes <= 4){
  301.             for(i = 0; i < ep->length; i++)
  302.                 lp[i] = ttohs(ti, ep->offset >> (16 - (i * 16)));
  303.         } else {
  304.             sp = (unsigned short *) NewPtr(nbytes);
  305.             if(sp == 0)
  306.                 goto bad;
  307.             if(tiff_read(ti, ttohl(ti, ep->offset), (char *)sp, nbytes) != 0)
  308.                 goto bad;
  309.             for(i = 0; i < ep->length; i++)
  310.                 lp[i] = ttohs(ti, sp[i]);
  311.         }
  312.     } else if(ep->type == TIFF_LONG){
  313.         nbytes = ep->length * 4;
  314.         if(nbytes <= 4){
  315.             lp[0] = ttohl(ti, ep->offset);
  316.         } else {
  317.             if(tiff_read(ti, ttohl(ti, ep->offset), (char *)lp, nbytes) != 0)
  318.                 goto bad;
  319.             for(i = 0; i < ep->length; i++)
  320.                 lp[i] = ttohl(ti, lp[i]);
  321.         }
  322.     } else {
  323.         goto bad;
  324.     }
  325.     
  326.     if(cp)
  327.         DisposPtr( ( Ptr ) cp);
  328.     if(sp)
  329.         DisposPtr( ( Ptr )  sp);
  330.     return(lp);
  331.     
  332. bad:
  333.     TIFFError(ti, -1, "\pInvalid array field.");
  334.     if(cp)
  335.         DisposPtr( ( Ptr ) cp);
  336.     if(sp)
  337.         DisposPtr( ( Ptr ) sp);
  338.     if(lp)
  339.         DisposPtr( ( Ptr ) lp);
  340.     return(0);
  341. }
  342.  
  343. /* convert depth (<= 8) to # of colors */
  344. short ColorsOfDepth[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256 };
  345.  
  346. /*
  347.  * return a color map with ngrays shades of gray in it.
  348.  */
  349. CTabHandle
  350. MakeGrayTable(short depth, short zeroIsWhite)
  351. {
  352.     short i, ncolors;
  353.     CTabHandle cth;
  354.     RGBColor rgb;
  355.     
  356.     if(HasColorQD() == 0)
  357.         return(0);
  358.         
  359.     if(depth >= 1 && depth <= 8)
  360.         ncolors = ColorsOfDepth[depth];
  361.     else
  362.         return(0);
  363.         
  364.     cth = (CTabHandle) NewHandle((ncolors * sizeof(ColorSpec)) + 10);
  365.     if(cth == 0)
  366.         return(0);
  367.         
  368.     (*cth)->ctSeed = GetCTSeed();
  369.     (*cth)->ctFlags = 0;
  370.     (*cth)->ctSize = ncolors - 1;
  371.     
  372.     if(zeroIsWhite){
  373.         for(i = 0; i < ncolors; i++){
  374.             rgb.red = rgb.green = rgb.blue = (65535 / (ncolors - 1)) * (ncolors - i - 1);
  375.             (*cth)->ctTable[i].value = i; /* this must be filled in... */
  376.             (*cth)->ctTable[i].rgb = rgb;
  377.         }
  378.     } else {
  379.         /* this doesn't work so hot on the Mac */
  380.         for(i = 0; i < ncolors; i++){
  381.             rgb.red = rgb.green = rgb.blue = (65535 / (ncolors - 1)) * i;
  382.             (*cth)->ctTable[i].value = i; /* this must be filled in... */
  383.             (*cth)->ctTable[i].rgb = rgb;
  384.         }
  385.     }
  386.     
  387.     return(cth);
  388. }
  389.  
  390. /*
  391.  * Turn a TIFF color table, which has red values, then green values, then blue values,
  392.  * into a QuickDraw ColorTable.
  393.  */
  394. CTabHandle
  395. TranslateColorMap(long *colorMap, short ncolors)
  396. {
  397.     short i;
  398.     CTabHandle cth;
  399.     RGBColor rgb;
  400.     
  401.     if(HasColorQD() == 0)
  402.         return(0);
  403.         
  404.     if(colorMap == 0 || ncolors <= 0 || ncolors > 256)
  405.         return(0);
  406.         
  407.     cth = (CTabHandle) NewHandle((ncolors * sizeof(ColorSpec)) + 10);
  408.     if(cth == 0)
  409.         return(0);
  410.         
  411.     (*cth)->ctSeed = GetCTSeed();
  412.     (*cth)->ctFlags = 0;
  413.     (*cth)->ctSize = ncolors - 1;
  414.     
  415.     for(i = 0; i < ncolors; i++){
  416.         rgb.red = colorMap[i];
  417.         rgb.green = colorMap[i + ncolors];
  418.         rgb.blue = colorMap[i + ncolors + ncolors];
  419.         (*cth)->ctTable[i].value = i; /* this must be filled in... */
  420.         (*cth)->ctTable[i].rgb = rgb;
  421.     }
  422.     
  423.     return(cth);
  424.  
  425. }
  426.  
  427. long
  428. ttohs(TIFFPtr ti, short s)
  429. {
  430.     long s1;
  431.     
  432.     if(ti->byteOrder == BigEndian){
  433.         /* Mac-order */
  434.         return(s & 0xffff);
  435.     } else {
  436.         s1 = (s >> 8) & 0xff;
  437.         s1 |= (s & 0xff) << 8;
  438.         return(s1);
  439.     }
  440. }
  441.  
  442. long
  443. ttohl(TIFFPtr ti, long l)
  444. {
  445.     long l1;
  446.     short i;
  447.     
  448.     if(ti->byteOrder == BigEndian){
  449.         /* Mac-order */
  450.         return(l);
  451.     } else {
  452.         l1 = 0;
  453.         for(i = 0; i < 4; i++){
  454.             l1 <<= 8;
  455.             l1 |= (l & 0xff);
  456.             l >>= 8;
  457.         }
  458.         return(l1);
  459.     }
  460. }
  461.  
  462. OSErr
  463. tiff_read(TIFFPtr ti, unsigned long offset, void *buf, long n)
  464. {
  465.     short err;
  466.     long count;
  467.     
  468.     err = SetFPos(ti->ref, fsFromStart, offset);
  469.     if(err < 0){
  470.         TIFFError(ti, err, "\pSeek failed.");
  471.         return(err);
  472.     }
  473.         
  474.     count = n;
  475.     err = FSRead(ti->ref, &count, buf);
  476.     if(err < 0 && err != eofErr){
  477.         TIFFError(ti, err, "\pRead failed.");
  478.         return(err);
  479.     }
  480.     if(count != n){
  481.         TIFFError(ti, -1, "\pRead returned too little data.");
  482.         return(eofErr);
  483.     }
  484.         
  485.     return(0);
  486. }
  487.  
  488. PixMapHandle
  489. PalettePixMap(short depth, CTabHandle cth, long x, long y, long width, long height,
  490.               Ptr data, long row)
  491. {
  492.     PixMapHandle pm;
  493.     
  494.     if(HasColorQD() == 0)
  495.         return(0);
  496.         
  497.     /* Enforce restrictions. */
  498.     if((row & 1) != 0 ||
  499.        (((long) data) & 1) != 0 ||
  500.        (depth != 1 && depth != 2 && depth != 4 && depth != 8) ||
  501.        cth == 0){
  502.            return(0);
  503.     }
  504.     
  505.     pm = NewPixMap();
  506.     if(pm == 0)
  507.         return(0);
  508.         
  509.     (*pm)->pmVersion = 0; /* this is crucial */
  510.     (*pm)->pmReserved = 0;
  511.     (*pm)->packType = 0;
  512.     (*pm)->packSize = 0;
  513.     (*pm)->pixelType = 0; /* chunky */
  514.     (*pm)->pixelSize = depth; /* bits per pixel */
  515.     (*pm)->cmpCount = 1; /* 1 component per pixel */
  516.     (*pm)->cmpSize = depth; /* bits per component */
  517.     (*pm)->planeBytes = 0;
  518.     
  519.     (*pm)->bounds.left = x;
  520.     (*pm)->bounds.right = x + width;
  521.     (*pm)->bounds.top = y;
  522.     (*pm)->bounds.bottom = y + height;
  523.     
  524.     (*pm)->baseAddr = data;
  525.     (*pm)->rowBytes = row | 0x8000; /* the 0x8000 marks this as a Pixmap, not Bitmap */
  526.     
  527.     if((*pm)->pmTable == 0){
  528.         DisposPixMap(pm);
  529.         return(0);
  530.     }
  531.         
  532.     /*
  533.      * Give the PixMap its own color table, which DisposPixMap() frees.
  534.      */
  535.     MoveHHi( ( Handle ) cth);
  536.     HLock( ( Handle ) cth);
  537.     PtrToXHand(*cth, ( Handle ) (*pm)->pmTable, GetHandleSize( ( Handle ) cth));
  538.     HUnlock( ( Handle ) cth);
  539.     
  540.     return(pm);
  541. }
  542.  
  543. PixMapHandle RGBPixMap(short depth, long x, long y, long width, long height,
  544.           Ptr data, long rowbytes)
  545. {
  546.     PixMapHandle pm;
  547.     
  548.     if(HasQD32() == 0)
  549.         return(0);
  550.         
  551.     /* Enforce restrictions. */
  552.     if((rowbytes & 1) != 0 ||
  553.        (((long) data) & 1) != 0 ||
  554.        (depth != 16 && depth != 32)){
  555.            return(0);
  556.     }
  557.     
  558.     pm = NewPixMap();
  559.     if(pm == 0)
  560.         return(0);
  561.         
  562.     (*pm)->pmVersion = 0; /* this is crucial */
  563.     (*pm)->pmReserved = 0;
  564.     (*pm)->packType = 0;
  565.     (*pm)->packSize = 0;
  566.     (*pm)->pixelType = 16; /* RGBDirect */
  567.     (*pm)->pixelSize = depth; /* bits per pixel */
  568.     (*pm)->cmpCount = 3; /* 1 component per pixel */
  569.     (*pm)->cmpSize = (depth == 32 ? 8 : 5); /* bits per component */
  570.     (*pm)->planeBytes = 0;
  571.     
  572.     (*pm)->bounds.left = x;
  573.     (*pm)->bounds.right = x + width;
  574.     (*pm)->bounds.top = y;
  575.     (*pm)->bounds.bottom = y + height;
  576.     
  577.     (*pm)->baseAddr = data;
  578.     (*pm)->rowBytes = rowbytes | 0x8000; /* the 0x8000 marks this as a Pixmap, not Bitmap */
  579.     
  580.     return(pm);
  581. }
  582.  
  583. /*
  584.  * Return (a*b)/c. Try to minimize the error.
  585.  */
  586. #define MulDiv(a, b, c) (((a) * (b)) / (c))
  587.  
  588. /*
  589.  * Convert a from coordinate world aref to world bref. Result in b.
  590.  * Optimized for aref and bref the same size.
  591.  */
  592. void ScaleRect(Rect *aref, Rect *a, Rect *bref, Rect *b)
  593. {
  594.     long hmul, hdiv, hoff;
  595.     long vmul, vdiv, voff;
  596.     
  597.     hmul = bref->right - bref->left;
  598.     hdiv = aref->right - aref->left;
  599.     
  600.     hoff = bref->left - MulDiv(aref->left, hmul, hdiv);
  601.     
  602.     vmul = bref->bottom - bref->top;
  603.     vdiv = aref->bottom - aref->top;
  604.     
  605.     voff = bref->top - MulDiv(aref->top, vmul, vdiv);
  606.     
  607.     b->left = MulDiv(a->left, hmul, hdiv) + hoff;
  608.     b->right = MulDiv(a->right, hmul, hdiv) + hoff;
  609.     b->top = MulDiv(a->top, vmul, vdiv) + voff;
  610.     b->bottom = MulDiv(a->bottom, vmul, vdiv) + voff;
  611. }
  612.  
  613. /*
  614.  * Calculate how many (uncompressed) bytes in a scanline.
  615.  * Doesn't work for separated planes.
  616.  * Returns -1 on error.
  617.  */
  618. long
  619. RowBytes(TIFFPtr ti)
  620. {
  621.     long rowbytes;
  622.     
  623.     if(ti->planarConfiguration != PCContiguous){
  624.         TIFFError(ti, -1, "\pCannot understand planar data.");
  625.         return(-1);
  626.     }
  627.         
  628.     rowbytes = (ti->bitsPerSample[0] * ti->samplesPerPixel * ti->imageWidth + 7) / 8;
  629.     
  630.     return(rowbytes);
  631. }
  632.  
  633. /*
  634.  * Read a reasonable number of scan lines from an image, uncompress them if
  635.  * necessary, and return a new pointer to the data. For compressed images,
  636.  * this routine always reads exactly one strip. Since some applications write
  637.  * large uncompressed images as a single strip, this routine tries to read
  638.  * only about 64K at a time. ReadStrip() returns the number of lines actually
  639.  * read in *linesread, and the first line actually read in *firstread. The
  640.  * latter will always be <= startline.
  641.  */
  642. Ptr ReadStrip(TIFFPtr ti, long startline, long *firstread, long *linesread)
  643. {
  644.     long count, rowbytes, offset, uncount, i, nrows, strip;
  645.     long skip, suboffset;
  646.     Ptr p = 0, p1 = 0;
  647.     Ptr srcPtr, dstPtr;
  648.     
  649.     /*
  650.      * Decide which strip to read.
  651.      */
  652.     for(strip = 0; strip < ti->stripsPerImage; strip++)
  653.         if(startline >= strip*ti->rowsPerStrip && startline < (strip+1)*ti->rowsPerStrip)
  654.             break;
  655.     if(strip >= ti->stripsPerImage){
  656.         TIFFError(ti, -1, "\pBad arg to ReadStrip()");
  657.         return(0);
  658.     }
  659.     
  660.     /*
  661.      * Decide how many rows in this strip; the last strip might have
  662.      * fewer than the others.
  663.      */
  664.     nrows = ti->rowsPerStrip;
  665.     if((strip * ti->rowsPerStrip) + nrows > ti->imageLength)
  666.         nrows = ti->imageLength - (strip * ti->rowsPerStrip);
  667.     
  668.     if(ti->stripOffsets == 0){
  669.         TIFFError(ti, -1, "\pNo strip offsets.");
  670.         return(0);
  671.     }
  672.     offset = ti->stripOffsets[strip];
  673.     
  674.     if(ti->compression == NoCompression){
  675.         /*
  676.          * Just read part of a strip.
  677.          */
  678.         if((rowbytes = RowBytes(ti)) == -1)
  679.             return(0);
  680.         skip = startline - (strip * ti->rowsPerStrip);
  681.         suboffset = offset + (skip * rowbytes);
  682.         nrows -= skip;
  683. #define BUFSIZE 64000L
  684.         if(rowbytes >= BUFSIZE){
  685.             nrows = 1;
  686.         } else if((nrows * rowbytes) > BUFSIZE){
  687.             nrows = BUFSIZE / rowbytes;
  688.         }
  689.         count = rowbytes * nrows;
  690.     
  691.         p = NewPtr(count);
  692.         if(p == 0){
  693.             TIFFError(ti, MemError(), "\pOut of memory.");
  694.             return(0);
  695.         }
  696.     
  697.         if(tiff_read(ti, suboffset, p, count) != 0)
  698.             goto bad;
  699.             
  700.         *firstread = startline;
  701.         *linesread = nrows;
  702.         
  703.         return(p);
  704.     }
  705.         
  706.     if(ti->stripByteCounts){
  707.         count = ti->stripByteCounts[strip];
  708.     } else {
  709.         /* cannot guess strip length */
  710.         return(0);
  711.     }        
  712.     
  713.     p = NewPtr(count);
  714.     if(p == 0){
  715.         TIFFError(ti, MemError(), "\pOut of memory.");
  716.         return(0);
  717.     }
  718.  
  719.     if(tiff_read(ti, offset, p, count) != 0)
  720.         goto bad;
  721.     
  722.     if(ti->compression == LZWCompression){
  723.         rowbytes = RowBytes(ti);
  724.         if(rowbytes == -1)
  725.             goto bad;
  726.         uncount = rowbytes * ti->rowsPerStrip;
  727.         p1 = NewPtr(uncount);
  728.         if(p1 == 0){
  729.             TIFFError(ti, MemError(), "\pOut of memory.");
  730.             goto bad;
  731.         }
  732.         
  733.         if(UnLZW(p, count, p1, uncount) != 0){
  734.             TIFFError(ti, -1, "\pLZW decompression failed.");
  735.             goto bad;
  736.         }
  737.         DisposPtr(p);
  738.         p = p1;
  739.         p1 = 0;
  740.         
  741.         if(ti->predictor == HDPredictor &&
  742.            ti->bitsPerSample[0] == 8 &&
  743.            ti->planarConfiguration == PCContiguous){
  744.             /* horizontal differencing */
  745.             for(i = 0; i < ti->samplesPerPixel; i++)
  746.                 UnDifference(p+i, rowbytes, ti->rowsPerStrip, ti->samplesPerPixel);
  747.         } else if(ti->predictor != NoPredictor){
  748.             TIFFError(ti, -1, "\pUnimplemented LZW prediction type.");
  749.             goto bad;
  750.         }
  751.     } else if(ti->compression == PackCompression){
  752.         rowbytes = RowBytes(ti);
  753.         if(rowbytes == -1)
  754.             goto bad;
  755.         if(rowbytes > 32767){
  756.             TIFFError(ti, -1, "\pRow too long for UnpackBits.");
  757.             goto bad;
  758.         }
  759.         uncount = rowbytes * ti->rowsPerStrip;
  760.         p1 = NewPtr(uncount);
  761.         if(p1 == 0){
  762.             TIFFError(ti, MemError(), "\pOut of memory.");
  763.             goto bad;
  764.         }
  765.         srcPtr = p;
  766.         for(i = 0; i < nrows; i++){
  767.             dstPtr = p1 + (i * rowbytes);
  768.             /* does UnpackBits restrict the size of rowbytes? */
  769.             UnpackBits(&srcPtr, &dstPtr, (short)rowbytes);
  770.         }
  771.         DisposPtr(p);
  772.         p = p1;
  773.         p1 = 0;
  774.     } else {
  775.         TIFFError(ti, -1, "\pUnimplemented compression type.");
  776.         goto bad;
  777.     }
  778.     
  779.     *firstread = strip * ti->rowsPerStrip;
  780.     *linesread = nrows;
  781.     
  782.     return(p);
  783.     
  784. bad:
  785.     if(p)
  786.         DisposPtr(p);
  787.     if(p1)
  788.         DisposPtr(p1);
  789.     return(0);
  790. }
  791.  
  792. /*
  793.  * Draw a tiff file image in the current QuickDraw port.
  794.  * The idea is to read each strip, convert into a form
  795.  * that's usable as a QuickDraw PixMap, and copy it to the
  796.  * current port. As an optimization, don't bother reading
  797.  * strips that won't be visible.
  798.  */
  799. OSErr
  800. DrawTIFF(short ref, TIFFPtr ti, Rect sr, Rect dr, short dither)
  801. {
  802.     long y, stripHeight, dstStripHeight, i, rowbytes;
  803.     Ptr p = 0, p1, p2;
  804.     PixMapHandle pm = 0;
  805.     Rect visr, portr;
  806.     GrafPtr port;
  807.     short ppb, inv;
  808.     BitMap bm;
  809.     
  810.     GetPort(&port);
  811.     
  812.     /*
  813.      * Make sure we know how many bits there are per sample, and that
  814.      * the Red, Green, and Blue channels are the same size for RGB images.
  815.      */
  816.     if(ti->bitsPerSample == 0){
  817.         TIFFError(ti, -1, "\pNo bits-per-sample given.");
  818.         return(-1);
  819.     }
  820.     for(i = 1; i < ti->samplesPerPixel; i++){
  821.         if(ti->bitsPerSample[i] != ti->bitsPerSample[i-1]){
  822.             TIFFError(ti, -1, "\pBits-per-sample are different.");
  823.             return(-1);
  824.         }
  825.     }
  826.     
  827.     ti->ref = ref;
  828.     
  829.     /*
  830.      * The destination rectangle may not be totally visible. As an optimization,
  831.      * translate the bounding box of the visible area into the source image
  832.      * space, and only copy the relevant strips.
  833.      */
  834.     portr = (*(port->visRgn))->rgnBBox;
  835.     ScaleRect(&dr, &portr, &sr, &visr);
  836.     y = visr.top;
  837.     stripHeight = 0;
  838.  
  839.     for( ; y < visr.bottom && y < ti->imageLength; y += stripHeight){
  840.         p = ReadStrip(ti, y, &y, &stripHeight);
  841.         if(p == 0)
  842.             goto bad;
  843.          
  844.         /*
  845.          * Find a way to draw the strip.
  846.          */
  847.         if(ti->samplesPerPixel == 1 &&
  848.            ti->bitsPerSample[0] == 1 &&
  849.            (ti->photometricInterpretation == PIZeroIsBlack ||
  850.             ti->photometricInterpretation == PIZeroIsWhite)){
  851.             rowbytes = (ti->bitsPerSample[0] * ti->imageWidth + 7) / 8;
  852.             inv = ti->photometricInterpretation == PIZeroIsBlack;
  853.             if(rowbytes & 1){
  854.                 bm.bounds.right = ti->imageWidth;
  855.                 bm.rowBytes = rowbytes + 1;
  856.                 for(i = 0; i < stripHeight; i++){
  857.                     p1 = p + (i * rowbytes);
  858.                     bm.bounds.top = y + i;
  859.                     bm.bounds.bottom = y + i + 1;
  860.                     if((long)p1 & 1){
  861.                         bm.bounds.left = -8;
  862.                         bm.baseAddr = p1 - 1;
  863.                     } else {
  864.                         bm.bounds.left = 0;
  865.                         bm.baseAddr = p1;
  866.                     }
  867.                     CopyBMStrip(&bm, sr, dr, inv);
  868.                 }
  869.             } else {
  870.                 bm.baseAddr = p;
  871.                 bm.rowBytes = rowbytes;
  872.                 bm.bounds.left = 0;
  873.                 bm.bounds.right = ti->imageWidth;
  874.                 bm.bounds.top = y;
  875.                 bm.bounds.bottom = y + stripHeight;
  876.                 CopyBMStrip(&bm, sr, dr, inv);
  877.             }            
  878.         } else if(ti->samplesPerPixel == 3 &&
  879.            ti->bitsPerSample[0] == 8 &&
  880.            ti->planarConfiguration == PCContiguous &&
  881.            ti->photometricInterpretation == PIRGB){
  882.             /* convert from 3 to 4 bytes per pixel */
  883.             p1 = ThreeToFour(p, ti->imageWidth * stripHeight);
  884.             if(p1 == 0){
  885.                 TIFFError(ti, -1, "\pOut of memory.");
  886.                 goto bad;
  887.             }
  888.             rowbytes = 4 * ti->imageWidth;
  889.             pm = RGBPixMap(32, 0, y, ti->imageWidth, stripHeight, p1, rowbytes);
  890.             if(pm == 0){
  891.                 DisposPtr(p1);
  892.                 TIFFError(ti, -1, "\pCould not allocate PixMap.");
  893.                 goto bad;
  894.             }
  895.             CopyPMStrip(pm, sr, dr, dither);
  896.             DisposPixMap(pm);
  897.             pm = 0;
  898.             DisposPtr(p1);
  899.         } else if(ti->samplesPerPixel == 1 &&
  900.            (ti->bitsPerSample[0] == 1 || ti->bitsPerSample[0] == 2 ||
  901.             ti->bitsPerSample[0] == 4 || ti->bitsPerSample[0] == 8) &&
  902.            (ti->photometricInterpretation == PIPalette ||
  903.             ti->photometricInterpretation == PIZeroIsBlack ||
  904.             ti->photometricInterpretation == PIZeroIsWhite)){
  905.             rowbytes = (ti->bitsPerSample[0] * ti->imageWidth + 7) / 8;
  906.             if(rowbytes & 1){
  907.                 /* must copy odd widths a line at a time */
  908.                 ppb = 8 / ti->bitsPerSample[0];
  909.                 pm = PalettePixMap(ti->bitsPerSample[0], ti->colorMap,
  910.                                    0, 0,
  911.                                    ti->imageWidth, 1,
  912.                                    0, rowbytes + 1);
  913.                 if(pm == 0){
  914.                     TIFFError(ti, -1, "\pCould not allocate PixMap.");
  915.                     goto bad;
  916.                 }
  917.                 for(i = 0; i < stripHeight; i++){
  918.                     p1 = p + (i * rowbytes);
  919.                     (*pm)->bounds.top = y + i;
  920.                     (*pm)->bounds.bottom = y + i + 1;
  921.                     if((long)p1 & 1){
  922.                         (*pm)->bounds.left = -ppb;
  923.                         (*pm)->baseAddr = p1 - 1;
  924.                     } else {
  925.                         (*pm)->bounds.left = 0;
  926.                         (*pm)->baseAddr = p1;
  927.                     }
  928.                     CopyPMStrip(pm, sr, dr, dither);
  929.                 }
  930.                 DisposPixMap(pm);
  931.                 pm = 0;
  932.             } else {
  933.                 pm = PalettePixMap(ti->bitsPerSample[0], ti->colorMap,
  934.                                    0, y,
  935.                                    ti->imageWidth, stripHeight,
  936.                                    p, rowbytes);
  937.                 if(pm == 0){
  938.                     TIFFError(ti, -1, "\pCould not allocate PixMap.");
  939.                     goto bad;
  940.                 }
  941.                 CopyPMStrip(pm, sr, dr, dither);
  942.                 DisposPixMap(pm);
  943.                 pm = 0;
  944.             }
  945.         } else if (ti->samplesPerPixel == 1 &&            // NA - new case: 16 bit grey
  946.                                ti->bitsPerSample[0] == 16 &&
  947.                                ti->compression == NoCompression &&
  948.                                ti->photometricInterpretation == PIZeroIsWhite &&
  949.                                ti->planarConfiguration == PCContiguous) {
  950.                                
  951.             /* convert from 2 to 1 bytes per pixel */
  952.             p1 = TwoToOneByte(ti, p, ti->imageWidth * stripHeight);
  953.             if(p1 == 0){
  954.                 TIFFError(ti, -1, "\pOut of memory.");
  955.                 goto bad;
  956.             }
  957.             
  958.             if (ti->colorMap == NULL)
  959.                 ti->colorMap = MakeGrayTable(8,
  960.                                  ti->photometricInterpretation == PIZeroIsWhite);
  961.  
  962.             rowbytes = (8 * ti->imageWidth + 7) / 8;
  963.             if(rowbytes & 1){
  964.                 /* must copy odd widths a line at a time */
  965.                 ppb = 1;
  966.                 pm = PalettePixMap(8, ti->colorMap,
  967.                                    0, 0,
  968.                                    ti->imageWidth, 1,
  969.                                    0, rowbytes + 1);
  970.                 if(pm == 0){
  971.                     TIFFError(ti, -1, "\pCould not allocate PixMap.");
  972.                     goto bad;
  973.                 }
  974.                 for(i = 0; i < stripHeight; i++){
  975.                     p2 = p1 + (i * rowbytes);
  976.                     (*pm)->bounds.top = y + i;
  977.                     (*pm)->bounds.bottom = y + i + 1;
  978.                     if((long)p2 & 1){
  979.                         (*pm)->bounds.left = -ppb;
  980.                         (*pm)->baseAddr = p2 - 1;
  981.                     } else {
  982.                         (*pm)->bounds.left = 0;
  983.                         (*pm)->baseAddr = p2;
  984.                     }
  985.                     CopyPMStrip(pm, sr, dr, dither);
  986.                 }
  987.                 DisposPixMap(pm);
  988.                 pm = 0;
  989.             } else {
  990.                 pm = PalettePixMap(8, ti->colorMap,
  991.                                    0, y,
  992.                                    ti->imageWidth, stripHeight,
  993.                                    p1, rowbytes);
  994.                 if(pm == 0){
  995.                     TIFFError(ti, -1, "\pCould not allocate PixMap.");
  996.                     goto bad;
  997.                 }
  998.                 
  999.                 CopyPMStrip(pm, sr, dr, dither);
  1000.                 DisposPixMap(pm);
  1001.                 pm = 0;
  1002.             }
  1003.             
  1004.         } else {
  1005.             TIFFError(ti, -1, "\pUnimplemented image representation.");
  1006.             goto bad;
  1007.         }        
  1008.             
  1009.         DisposPtr(p);
  1010.         p = 0;
  1011.     }
  1012.     
  1013.     return(0);
  1014.     
  1015. bad:
  1016.     if(p)
  1017.         DisposPtr(p);
  1018.     if(pm)
  1019.         DisposPixMap(pm);
  1020.     return(-1);
  1021. }
  1022.  
  1023. /*
  1024.  * Copy a pixmap of a strip to the right place.
  1025.  */
  1026. void
  1027. CopyPMStrip(PixMapHandle pm, Rect sr, Rect dr, short dither)
  1028. {
  1029.     Rect sr1, dr1;
  1030.     GrafPtr port;
  1031.  
  1032.     sr1 = (*pm)->bounds;
  1033.     sr1.left = sr.left;
  1034.     sr1.right = sr.right;
  1035.     if(sr1.top < sr.top)
  1036.         sr1.top = sr.top;
  1037.     if(sr1.bottom > sr.bottom)
  1038.         sr1.bottom = sr.bottom;
  1039.         
  1040.     ScaleRect(&sr, &sr1, &dr, &dr1);
  1041.     
  1042.     GetPort(&port);
  1043.     
  1044.     MoveHHi( ( Handle ) pm);
  1045.     HLock( ( Handle ) pm);
  1046.     CopyBits( ( BitMap * ) *pm, ( BitMap * ) &(port->portBits), &sr1, &dr1, dither ? 64 : srcCopy, 0L);
  1047.     HUnlock( ( Handle ) pm);
  1048. }
  1049.  
  1050. /*
  1051.  * Copy a bitmap of a strip to the right place.
  1052.  * Optionally invert black and white.
  1053.  */
  1054. void
  1055. CopyBMStrip(BitMap *bm, Rect sr, Rect dr, short inv)
  1056. {
  1057.     Rect sr1, dr1;
  1058.     GrafPtr port;
  1059.  
  1060.     sr1 = bm->bounds;
  1061.     sr1.left = sr.left;
  1062.     sr1.right = sr.right;
  1063.     if(sr1.top < sr.top)
  1064.         sr1.top = sr.top;
  1065.     if(sr1.bottom > sr.bottom)
  1066.         sr1.bottom = sr.bottom;
  1067.         
  1068.     ScaleRect(&sr, &sr1, &dr, &dr1);
  1069.     
  1070.     GetPort(&port);
  1071.     
  1072.     CopyBits(( BitMap * ) bm, ( BitMap * ) &(port->portBits), &sr1, &dr1, inv ? notSrcCopy : srcCopy, 0L);
  1073. }
  1074.  
  1075.  
  1076. /*
  1077.  * Convert 3-bytes-per-pixel RGB data to 4-bytes-per-pixel, as required
  1078.  * for QuickDraw 32-bit Pix Maps. inLen is the number of bytes.
  1079.  */
  1080. Ptr
  1081. ThreeToFour(Ptr in, long nPixels)
  1082. {
  1083.     Ptr out;
  1084.     register unsigned long *inp, *outp;
  1085.     register unsigned long in1, in2, in3;
  1086.     char *ip, *op;
  1087.     
  1088.     out = NewPtr(4 * nPixels);
  1089.     if(out == 0)
  1090.         return(0);
  1091.         
  1092.     inp = (unsigned long *) in;
  1093.     outp = (unsigned long *) out;
  1094.     
  1095.     for( ; nPixels >= 4; nPixels -= 4){
  1096.         /* read 4 pixels worth of data in 4 longs: RGBR GBRG BRGB */
  1097.         in1 = *inp++;
  1098.         in2 = *inp++;
  1099.         in3 = *inp++;
  1100.         
  1101.         /* write the 4 pixels as 4 longs */
  1102.         *outp++ = in1 >> 8;
  1103.         *outp++ = ((in1 & 0xff) << 16) | ((in2 >> 16) & 0xffff);
  1104.         *outp++ = ((in2 & 0xffff) << 8) | (in3 >> 24);
  1105.         *outp++ = in3 & 0xffffff;
  1106.     }
  1107.     
  1108.     /* take care of last 1, 2 or 3 pixels one byte at a time */
  1109.     ip = (char *) inp;
  1110.     op = (char *) outp;
  1111.     for( ; nPixels > 0; --nPixels){
  1112.         *op++ = 0;
  1113.         *op++ = *ip++;
  1114.         *op++ = *ip++;
  1115.         *op++ = *ip++;
  1116.     }
  1117.     
  1118.     return(out);
  1119. }
  1120.  
  1121. /* - NA
  1122.  * Convert 2-bytes-per-pixel pixel data to 1-byte-per-pixel, as required
  1123.  * for QuickDraw 8-bit Pix Maps. inLen is the number of bytes.
  1124.  */
  1125. Ptr
  1126. TwoToOneByte(TIFFPtr ti, Ptr in, long nPixels)
  1127. {
  1128.     Ptr out;
  1129.     register unsigned short *inp;
  1130.     register unsigned char *outp;
  1131.     register unsigned long in1, in2, in3;
  1132.     char *ip, *op;
  1133.     
  1134.     out = NewPtr(nPixels);
  1135.     if(out == 0)
  1136.         return(0);
  1137.         
  1138.     inp = (unsigned short *) in;
  1139.     outp = (unsigned char *) out;
  1140.     
  1141.     for (long index = 0; index < nPixels; index++)
  1142.         //*(outp++) = (unsigned char)((*(inp++) >> 8) & 0xFF);
  1143.         *(outp++) = (unsigned char)((ttohs(ti, *(inp++)) >> 8) & 0xFF);
  1144.     
  1145.     return(out);
  1146. }
  1147.  
  1148.  
  1149. /*
  1150.  * Un-difference a strip of image. Must be 8-bit samples.
  1151.  * Span indicates how many bytes to skip (for interleaved RGB).
  1152.  * Modifies the data in place.
  1153.  */
  1154. void
  1155. UnDifference(Ptr p, long rowbytes, long nrows, long span)
  1156. {
  1157.     long row, col, off;
  1158.     
  1159.     for(row = 0; row < nrows; row++){
  1160.         off = row * rowbytes;
  1161.         for(col = span; col < rowbytes; col += span){
  1162.             p[off + col] += p[off + col - span];
  1163.         }
  1164.     }
  1165. }
  1166.  
  1167. /*
  1168.  * Register an error with a TIFF record, for later retrieval with GetTIFFError().
  1169.  */
  1170. void
  1171. TIFFError(TIFFPtr ti, OSErr err, StringPtr p)
  1172. {
  1173.     short i;
  1174.     
  1175.     if(ti && ti->err == 0){
  1176.         if(err == 0)
  1177.             err = -1;
  1178.         ti->err = err;
  1179.         for(i = 0; i < p[0] && i < 255; i++)
  1180.             ti->errStr[i+1] = p[i+1];
  1181.         ti->errStr[0] = p[0];
  1182.     }
  1183. }
  1184.  
  1185. /*
  1186.  * Return and clear the error associated with a TIFF record.
  1187.  */
  1188. OSErr
  1189. GetTIFFError(TIFFPtr ti, StringPtr p)
  1190. {
  1191.     short i;
  1192.     OSErr err;
  1193.     
  1194.     p[0] = 0;
  1195.     if(ti == 0)
  1196.         return(-1);
  1197.         
  1198.     p[0] = ti->errStr[0];
  1199.     for(i = 0; i < p[0] && i < 255; i++)
  1200.         p[i+1] = ti->errStr[i+1];
  1201.         
  1202.     err = ti->err;
  1203.     ti->err = 0;
  1204.     ti->errStr[0] = 0;
  1205.     
  1206.     return(err);
  1207. }
  1208.  
  1209. /*
  1210.  * Can a TIFF image be drawn with this system's version of QuickDraw?
  1211.  */
  1212. Boolean
  1213. TIFFDrawable(TIFFPtr ti)
  1214. {
  1215.     if(ti->samplesPerPixel > 1)
  1216.         return(HasQD32());
  1217.     if(ti->bitsPerSample[0] > 1)
  1218.         return(HasColorQD());
  1219.     return(TRUE);
  1220. }
  1221.  
  1222.  
  1223. Boolean HasQD32( )
  1224. {
  1225.     long            answer;
  1226.     short            gQDVersion;
  1227.     
  1228.     
  1229.     gQDVersion = ( GetGestaltResult( gestaltQuickdrawVersion ) >> 8 ) & 0xFF;
  1230.             
  1231.     if ( gQDVersion == 2 )
  1232.     {
  1233.         return ( TRUE );
  1234.     }
  1235.     else
  1236.     {
  1237.         return ( FALSE );
  1238.     }
  1239. }
  1240.  
  1241.  
  1242. Boolean HasColorQD( )
  1243. {
  1244.     long            answer;
  1245.     
  1246.     
  1247.     if ( Gestalt( gestaltQuickdrawVersion, &answer ) == noErr )
  1248.     {
  1249.         if ( BitTst( &answer, gestaltHasColor ) == noErr )
  1250.         {
  1251.             return ( TRUE );
  1252.         }
  1253.         else
  1254.         {
  1255.             return ( FALSE );
  1256.         }
  1257.     }
  1258. }
  1259.  
  1260.  
  1261. /************************************************************************
  1262. ************************************************************************/
  1263.  
  1264. /* 
  1265.  * GetGestaltResult returns the result value from Gestalt for the specified
  1266.  * selector.  If Gestalt returned an error GetGestaltResult returns zero.
  1267.  * Use of this function is only cool if we don't care whether Gestalt returned
  1268.  * an error.  In many cases you may need to know the exact Gestalt error code
  1269.  * so then this function would be inappropriate.
  1270.  */
  1271.  
  1272. static long GetGestaltResult( OSType gestaltSelector )
  1273. {
  1274.     long    gestaltResult = 0;
  1275.  
  1276.  
  1277.     if ( Gestalt( gestaltSelector, &gestaltResult ) == noErr )
  1278.     {
  1279.         return ( gestaltResult );
  1280.     }
  1281.     else
  1282.     {
  1283.         return ( gestaltResult );
  1284.     }
  1285. }